package com.imooc.cloud.mall.practice.common.exception;

import com.imooc.cloud.mall.practice.common.common.ApiRestResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


/**
 * 全局异常处理类
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理所有异常
     *
     * @param e 异常对象
     * @return ApiRestResponse 返回统一的错误响应
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Object handleException(Exception e) {
        logger.error("Default Exception: ", e);
        return ApiRestResponse.error(ImoocMallExceptionEnum.SYSTEM_ERROR);
    }

    /**
     * 处理自定义异常
     *
     * @param e 自定义异常对象
     * @return ApiRestResponse 返回统一的错误响应
     */
    @ExceptionHandler(value = ImoocMallException.class)
    @ResponseBody
    public Object handleImoocMallException(ImoocMallException e) {
        logger.error("ImoocMallException: ", e);
        return ApiRestResponse.error(e.getCode(), e.getMessage());
    }

    /**
     * 处理MethodArgumentNotValidException异常
     * 主要处理@RequestBody上的@Valid注解验证失败产生的异常
     *
     * @param e MethodArgumentNotValidException异常
     * @return ApiRestResponse 返回统一的错误响应
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        logger.error("MethodArgumentNotValidException: ", e);
        return handleBindingResult(e.getBindingResult());
    }

    /**
     * 处理BindException异常
     * 主要处理@ModelAttribute表单绑定验证失败产生的异常
     *
     * @param e BindException异常
     * @return ApiRestResponse 返回统一的错误响应
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public Object handleBindException(BindException e) {
        logger.error("BindException: ", e);
        return handleBindingResult(e.getBindingResult());
    }

    /**
     * 处理ConstraintViolationException异常
     * 主要处理在方法参数上直接使用注解验证失败产生的异常
     *
     * @param e ConstraintViolationException异常
     * @return ApiRestResponse 返回统一的错误响应
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseBody
    public Object handleConstraintViolationException(ConstraintViolationException e) {
        logger.error("ConstraintViolationException: ", e);
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        StringBuilder builder = new StringBuilder();
        Iterator<ConstraintViolation<?>> iterator = violations.iterator();
        while (iterator.hasNext()) {
            ConstraintViolation<?> violation = iterator.next();
            builder.append(violation.getPropertyPath()).append(":").append(violation.getMessage());
            if (iterator.hasNext()) {
                builder.append("; ");
            }
        }
        return ApiRestResponse.error(ImoocMallExceptionEnum.REQUEST_PARAM_ERROR.getCode(), builder.toString());
    }

    /**
     * 处理BindingResult，提取错误信息
     *
     * @param bindingResult 绑定结果，包含错误信息
     * @return ApiRestResponse 返回统一的错误响应
     */
    private ApiRestResponse handleBindingResult(BindingResult bindingResult) {
        List<String> errorList = new ArrayList<>();
        if (bindingResult.hasErrors()) {
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                errorList.add(fieldError.getField() + ": " + fieldError.getDefaultMessage());
            }
        }
        String errorMessage = String.join("; ", errorList);
        return ApiRestResponse.error(ImoocMallExceptionEnum.REQUEST_PARAM_ERROR.getCode(), errorMessage);
    }
    
    /**
     * 处理MissingServletRequestParameterException异常
     * 主要处理@RequestParam注解的required=true时，参数缺失产生的异常
     *
     * @param e MissingServletRequestParameterException异常
     * @return ApiRestResponse 返回统一的错误响应
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    @ResponseBody
    public Object handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        logger.error("MissingServletRequestParameterException: ", e);
        String paramName = e.getParameterName();
        String message = String.format("参数'%s'不能为空", paramName);
        return ApiRestResponse.error(ImoocMallExceptionEnum.REQUEST_PARAM_ERROR.getCode(), message);
    }
}
